package solver

import (
	"fmt"
	"log"
	"slices"
	"strings"
)

func verifyNaked(house House, cellNums []int) (naked bool, cands []int) {
	boolCands := [9]bool{}
	for _, cellNum := range cellNums {
		for cand, candidate := range house.cells[cellNum].candidates {
			boolCands[cand] = boolCands[cand] || candidate
		}
	}

	candCount := 0
	for _, boolCand := range boolCands {
		if boolCand {
			candCount++
		}
	}
	naked = candCount == len(cellNums)

	if !naked {
		return
	}
	for cand, boolCand := range boolCands {
		if boolCand {
			cands = append(cands, cand)
		}
	}
	return
}

func cellNumsForNaked(house House, nakedCount int) []int {
	cellNums := []int{}
	for cellNum, cell := range house.cells {
		if cell.solved() || cell.candidateCount > nakedCount {
			continue
		}
		cellNums = append(cellNums, cellNum)
	}
	return cellNums
}

func clearupNakedHouse(house House, cellNums []int, cands []int) (used bool) {
	for i, cell := range house.cells {
		if cell.solved() || slices.Contains(cellNums, i) {
			continue
		}
		for _, cand := range cands {
			if cell.removeCandidate(cand) {
				used = true
			}
		}
	}
	return
}

func logForNakedHidden(nakedOrHidden string, house House, cellNums []int, cands []int) {
	var b strings.Builder

	switch house.HouseType() {
	case 0:
		fmt.Fprintf(&b, "r%vc", house.cells[0].rowNum)
	case 1:
		fmt.Fprintf(&b, "c%vr", house.cells[0].colNum)
	case 2:
		fmt.Fprintf(&b, "b%vn", house.cells[0].boxNum)
	}
	for _, cellNums := range cellNums {
		fmt.Fprintf(&b, "%v", cellNums)
	}
	fmt.Fprint(&b, " ")
	for _, cand := range cands {
		fmt.Fprintf(&b, "%v", cand+1)
	}
	switch len(cellNums) {
	case 2:
		fmt.Fprintf(&b, " %v Pair", nakedOrHidden)
	case 3:
		fmt.Fprintf(&b, " %v Triple", nakedOrHidden)
	case 4:
		fmt.Fprintf(&b, " %v Quadruple", nakedOrHidden)
	}
	log.Println(b.String())
}

func (g *Grid) nakedHouse(house House, nakedCount int) (used bool) {
	cellNums := cellNumsForNaked(house, nakedCount)
	subsets := combine(cellNums, nakedCount)
	for _, subset := range subsets {
		naked, cands := verifyNaked(house, subset)
		if !naked {
			continue
		}
		if clearupNakedHouse(house, subset, cands) {
			logForNakedHidden("Naked", house, subset, cands)
			used = true
		}
	}
	return
}

func (g *Grid) NakedPairHouse(house House) (used bool) {
	return g.nakedHouse(house, 2)
}

func (g *Grid) NakedPair() (used bool) {
	return g.doStrategy(g.NakedPairHouse)
}

func (g *Grid) NakedTripleHouse(house House) (used bool) {
	return g.nakedHouse(house, 3)
}

func (g *Grid) NakedTriple() (used bool) {
	return g.doStrategy(g.NakedTripleHouse)
}

func (g *Grid) NakedQuadrupleHouse(house House) (used bool) {
	return g.nakedHouse(house, 4)
}

func (g *Grid) NakedQuadruple() (used bool) {
	return g.doStrategy(g.NakedQuadrupleHouse)
}
